#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G06 -- S08 ------------------------------------------------------------##
__all__ = []

from .. import autostacks
from .. import hint
from .. import pilelib as stack
from .. import stackfill as fill
from ..forms import (acesup as AcesUpLayout, klondike as KlondikeLayout,
                    streetsalleys as StreetsAlleysLayout, gypsy as GypsyLayout,)
from ..packlib import info as cs, place
from ..gamelib import game, info as _gi, state
from ..piles import (talon as TalonStack, foundation as FoundationStack,
                    camelot as CamelotStack, numerica as NumericaStack,)
from ..utillib.misc import kwdefault

GT, SL = _gi.GT, _gi.SL
Wrap = stack.StackWrapper


class Camelot(game.Game):
    """
    Drop 1 deck on foundation except court cards.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.acesup.AcesUpLayout
        - row [16 over 4 row N, no offset]
        - talon [1 NW, no offset]
        - foundation [1 NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.OpenAutoTalon
        Deal none. Build none. Play top on all row gap, before further row
        play. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SingleFoundation

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.camelot.Camelot_RowStack (base_rank=ANY)
        Build any card. Drop any or pair sum 10 on foundation. Play inner
        piles top, outer piles none. Base king corner, queen N S outer, jack
        E W outer, inner any.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.camelot.Camelot_Hint

    :note:
      ⟪quickplay⟫ ⟪autofilp⟫ ⟪autodrop⟫ disabled.
    """
    __info__ = dict(id=280, name='Camelot', game_type=(GT.PAIRING | GT.GNOME),
                    decks=1, skill_level=SL.BALANCED, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = AcesUpLayout.AcesUpLayout
    _talon_ = stack.OpenAutoTalon
    _foundation = stack.SingleFoundation
    _row_ = Wrap(CamelotStack.Camelot_RowStack, base_rank=cs.ANY)
    _hint_ = CamelotStack.Camelot_Hint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.
        Set rows base rank.

        .. \_create_game(rows=16, ncols=4, playcards=0, bases=[-1]) -> Layout
        """
        kwdefault(kw, rows=16, ncols=4, playcards=0, bases=[-1])
        l, s = super(Camelot, self)._create_game(**kw), self.s
        self._save_stack_caps([s['rows'][i] for i in (0, 3, 12, 15)],
                              base_rank=cs.KING, max_move=0)
        self._save_stack_caps([s['rows'][i] for i in (1, 2, 13, 14)],
                              base_rank=cs.QUEEN, max_move=0)
        self._save_stack_caps([s['rows'][i] for i in (4, 7, 8, 11)],
                              base_rank=cs.JACK, max_move=0)
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[talon]: open.

        .. \_start([]) -> None

        *Variables*
          status : {bool} False
        """
        self.status = False
        self.s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Overide to confirm status of `s.rows` to enable / disable `s.talon`.
        """
        if stack is self.s['talon']:
            self.status = all([r.cards for r in self.s['rows']])

    @property
    def is_complete(self):
        s = self.s
        return not s['talon'].cards and all([not r.cards for r in s['rows']
                                             if r.caps['base_rank'] == cs.ANY])

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop and quickplay.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.none()


class GrandmammasPatience(game.Game):
    """
    Finish 2 deck on foundation

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.CrescentLayout
        - row [22 over 2 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - reserve [4 horz S, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.OpenAutoTalon
        Deal none. Build none. Play top. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.Vary_SS_Foundation (dir=-1,max_move=0),
          pysollib.stack.Vary_SS_Foundation (max_move=0)
        Init base per 1. Build in rank by same suit wrap. N down from base. S
        up from base +1. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.camelot.GrandmammasPatience_RowStack (max_accept=1,
          max_cards=2)
        Init 1 card. Build any from talon. Play top. Gap fill 1 card from
        talon. Max 2 card.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.ReserveStack
        Init 1 card. Build any. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.defaultHint
    """
    __info__ = dict(id=622, name='Grandmammaʼs Patience',
                    game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = KlondikeLayout.CrescentLayout
    _talon_ = stack.OpenAutoTalon
    _foundations_ = (Wrap(stack.Vary_SS_Foundation, dir=-1, max_move=0),
                     Wrap(stack.Vary_SS_Foundation, max_move=0),)
    _row_ = Wrap(CamelotStack.GrandmammasPatience_RowStack, max_accept=1,
                 max_cards=2)
    _reserve_ = stack.ReserveStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.
        Set foundation texts. Add talon to dropstacks.

        .. \_create_game(rows=22, ncols=11, playcards=4, reserves=4) -> Layout
        """
        kwdefault(kw, rows=22, ncols=11, playcards=4, reserves=4)
        l, s = super(GrandmammasPatience, self)._create_game(**kw), self.s
        l.set_misc_text(s['foundations'][0], 'sw', text=_('Descending'))
        l.set_misc_text(s['foundations'][4], 'sw', text=_('Ascending'))
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundation]: 1. Set base ranks.
          - s[rows]: 1 ea.
          - s[talon]: open.

        .. \_start([]) -> None
        """
        base_rank = self.s['talon'].TOPCARD['rank']
        self.s['talon'].dealBaseCards(ncards=1, saveinfo=False)
        self._save_stack_caps(self.s['foundations'][:4], base_rank=base_rank)
        self._save_stack_caps(self.s['foundations'][4:],
                              base_rank=(base_rank + 1) % 13)
        self.s['talon'].dealRow()
        self.s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        fill.fromTalon(self, stack, self.s['rows'])


class SlyFox(game.Game):
    """
    Finish cards on _foundation_ within 1 turn

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.SlyFoxLayout
        - row [20 over 4 row N, no offset]
        - talon [1 NW, no offset]
        - foundation [8 over 2 col NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.SlyFox_Talon
        Deal none. Play top of next 20 cards before row play again. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundations.SlyFox_Foundation
        Init ace per W, king per E. Build in rank by same suit wrap. W up. E
        down. Play top. Base ace W, king E.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.numerica.SlyFox_RowStack (max_accept=MAX)
        Init 1 card. Build any from talon. Play top. Gap fill 1 card from
        talon.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint

    :seealso:
      <http://en.wikipedia.org/wiki/Sly_Fox_%28solitaire%29>

    """
    __info__ = dict(id=610, name='Sly Fox', game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = GypsyLayout.SlyFoxLayout
    _talon_ = TalonStack.SlyFox_Talon
    _foundations_ = FoundationStack.SlyFox_Foundation
    _row_ = Wrap(NumericaStack.SlyFox_RowStack, max_cards=cs.MAX)
    _hint_ = NumericaStack.Hint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.
        Add talon to dropstacks.

        .. \_create_game(rows=20, ncols=5, playcards=0) -> Layout
        """
        kwdefault(kw, rows=20, ncols=5, playcards=0)
        l, s = super(SlyFox, self)._create_game(**kw), self.s
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...) -> seq(Card ...)

        Overide to returns ACE and KING of suit on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.OneAceKing(cards, place.MoveToTop)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundation]: 1 ea.
          - s[rows]: 1 ea.
          - s[talon]: open.

        .. \_start([]) -> None

        *Variables:*
          status : {int} 0
        """
        self.status = 0
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        """
        if self.status == 0:
            fill.fromTalon(self, stack, self.s['rows'])

    @state.not_state('is_preview')
    def _update_text(self):
        if not self.texts['misc']:
            return
        self.texts['misc'].config(text='{0}/20'.format(self.status))


class OpenSlyFox(SlyFox):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.SlyFoxLayout
        - row [20 over 2 row N, vert offset]
        - talon [1 NW, no offset]
        - foundation [8 over 2 col NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.SlyFox_Talon
        Deal none. Play top of next 20 cards before row play again. No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundations.SlyFox_Foundation
        Init ace per W, king per E. Build in rank by same suit wrap. W up. E
        down. Play top. Base ace W, king E.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.numerica.SlyFox_RowStack (max_accept=MAX)
        Init 1 card. Build any from talon. Play top. Gap fill 1 card from
        talon.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint
    """
    __info__ = dict(id=614, name='Open Sly Fox', game_type=(GT.NUMERICA |
                    GT.ORIGINAL), decks=2, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(ncols=10, playcards=6) -> Layout
        """
        kwdefault(kw, ncols=10, playcards=6)
        return super(OpenSlyFox, self)._create_game(**kw)


class PrincessPatience(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.streetsalleys.StreetsAlleysLayout
        - row [16 over 4 row E, 16 over 4 row W foundation , no offset]
        - talon [1 NW, no offset]
        - waste [1 E talon, no offset]
        - foundation [8 over 2 col N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card per waste on click. No redeal.

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 1 card per waste. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build in rank by same suit wrap. Play top.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.camelot.PrincessPatience_RowStack (max_move=1)
        Init 1 card. Build down in rank by same suit. Play top inner. Gap fill
        1 card from waste.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=623, name='Princess Patience',
                    game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = StreetsAlleysLayout.StreetsAlleysLayout
    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(CamelotStack.PrincessPatience_RowStack, max_move=1)
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=32, ncols=4) -> Layout
        """
        kwdefault(kw, rows=32, ncols=4)
        return super(PrincessPatience, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()


class DoubleLine(game.Game):
    """
    Finish cards on _foundation_ within 1 turn

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [20 over 2 row S foundation, horz offset]
        - talon [1 NW, no offset]
        - foundation [8 horz N, no offset]

    _row_
      Init deal 1 card. Build any from
      _waste_. max 2 cards. Play top. Gap deals 1 card from _waste_ else none.

    _talon_
      Deal 1 card on _waste_.

    _waste_
      Init deal 1 card. Build none.

    _foundation_
      Build in rank by same suit wrap. N up. S down. Play
      top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: DoubleLine_RowStack (max_cards=2, max_move=1,
      max_accept=1, base_rank=GAP)
    :cvar Hint _hint_: hint.defaultHint
    """
    __info__ = dict(id=702, name='Double Line', game_type=GT.NUMERICA,
                    decks=2, skill_level=SL.BALANCED,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = KlondikeLayout.KlondikeLayout
    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation,
                                        base_rank=cs.KING, dir=-1),)
    _row_ = Wrap(CamelotStack.DoubleLine_RowStack, max_cards=2, max_move=1,
                               max_accept=1, base_rank=cs.GAP)

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=20, ncols=10, playcards=3) -> Layout
        """
        kwdefault(kw, rows=20, ncols=10, playcards=3)
        return super(DoubleLine, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        fill.fromWasteTalon(self, stack, self.s['rows'])


PROVIDES = [Camelot, SlyFox, OpenSlyFox, PrincessPatience,
            GrandmammasPatience, DoubleLine]